#include <iostream>
#include "GLAllInOne.h"
#include "Systems.h"
#include "Graphic.h"
#include "Scene.h"
#include "Camera.h"

#include "Application.h"

namespace ManchyGL
{
	void Application::framebuffer_size_callback(GLFWwindow* wnd, int w, int h)
	{
		glViewport(0,0,w,h);
	}

	Application::Application()
	{
		_title = "";
		_width = 400;
		_height = 400;

		_targetFrameRate = 60;
		_targetFrameInterval = 1.0f / _targetFrameRate;

		_clearMask = Graphic::CLEAR_MASK_COLOR_DEPTH;
		_bgColor = vec4(0.0f);

		_isRunning = false;
		_destroyed = false;

		_window = NULL;
		_systems = new Systems();
		_scene = new Scene();
		_camera = new Camera();
		GameObject* go = _scene->NewGameObject("MainCamera");
		go->AddComponent(ComponentType_Camera, _camera);
	}

	Application::~Application()
	{
		_window = NULL;
		if (NULL != _systems)
		{
			delete _systems;
			_systems = NULL;
		}
		if (NULL != _scene)
		{
			delete _scene;
			_scene = NULL;
		}
		_camera = NULL;
	}

	GLFWwindow* Application::GetWindow()
	{
		return _window;
	}

	Scene* Application::GetScene()
	{
		return _scene;
	}

	Camera* Application::GetCamera()
	{
		return _camera;
	}

	 void Application::AddSystem(SystemBase* sys)
	{
		_systems->AddSystem(sys);
		sys->OnCreate(this);
	}

	void Application::SetTargetFrameRate(int value)
	{
		if (value == _targetFrameRate)
			return;
		_targetFrameRate = value;
		if (_targetFrameRate > 0)
			_targetFrameInterval = 1.0 / value;
	}

	int Application::GetTargetFrameRate()
	{
		return _targetFrameRate;
	}

	int Application::GetFrameCount()
	{
		return _frameCount;
	}

	float Application::GetTime()
	{
		return _time;
	}

	float Application::GetDeltaTime()
	{
		return _deltaTime;
	}

	void Application::SetTitle(const char* title)
	{
		_title = title;
	}

	void Application::SetSize(int width, int height)
	{
		_width = width;
		_height = height;
	}

	void Application::SetBgColor(float r, float g, float b, float a)
	{
		_bgColor.r = r;
		_bgColor.g = g;
		_bgColor.b = b;
		_bgColor.a = a;
	}

	void Application::SetBgColor(vec4 color)
	{
		_bgColor = color;
	}

	vec4 Application::GetBgColor()
	{
		return  _bgColor;
	}

	void Application::SetClearMask(int mask)
	{
		_clearMask = mask;
	}

	int Application::GetWidth()
	{
		return _width;
	}

	int Application::GetHeight()
	{
		return _height;
	}

	int Application::Run()
	{
		int result = Init();
		if (result != 0)
			return result;

		_isRunning = true;
		_time = glfwGetTime();
		_lastTime = _time;
		_deltaTime = 0.0f;

		_systems->OnAwake(this);
		_systems->OnStart();

		while (CheckGameLoop())
		{
			if (_destroyed)
				break;

			_time = glfwGetTime();

			if (_targetFrameRate < 1 || _time >(_lastTime + _targetFrameInterval))
			{
				++_frameCount;
				_lastTime = _time;
			}
			else
				continue;
			
			Graphic::ClearColor(_bgColor.r, _bgColor.g, _bgColor.b, _bgColor.a);
			Graphic::Clear(_clearMask);

			OnPreFrame();
			OnFrame();
			OnPostFrame();
			OnRender();

			Graphic::Swap(_window);
			Graphic::PollEvents();
		}

		_isRunning = false;
	}

	int Application::Init()
	{
		glfwInit();
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
		glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
		glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
		GLFWwindow* window = glfwCreateWindow(_width, _height, _title, NULL, NULL);
		if (window == NULL)
		{
			std::cout << "Failed to create GLFW window" << std::endl;
			glfwTerminate();
			return -1;
		}
		glfwMakeContextCurrent(window);
		_window = window;

		//Graphic::SetViewport(0, 0, _width, _height);
		
		glfwSetFramebufferSizeCallback(_window, framebuffer_size_callback);
		
		//glfwSetCursorPosCallback(window, processMouse_Inner);
		//glfwSetScrollCallback(window, processScroll_Inner)
		

		// glad: load all OpenGL function pointers
		// ---------------------------------------
		if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
		{
			std::cout << "Failed to initialize GLAD" << std::endl;
			return -1;
		}
		return 0;
	}

	

	bool Application::CheckGameLoop()
	{
		if (_window != NULL && !glfwWindowShouldClose(_window))
			return true;
		return false;
	}

	void Application::OnPreFrame()
	{
		_systems->OnPreUpdate(_time, _deltaTime);
	}

	void Application::OnFrame()
	{
		_systems->OnUpdate(_time, _deltaTime);
	}

	void Application::OnPostFrame()
	{
		_systems->OnLateUpdate(_time, _deltaTime);
	}

	void Application::OnRender()
	{
		_systems->OnRender();
	}

	
}